function Scope () {
    this.$$watchers = [];           // 私有变量, 是 { watchFn, listenerFn } 的集合
    this.$$lastDirtyWatch = null;   // 最后一个脏值 watcher
    this.$$asyncQueue = [];
    this.$$phase = null;
    this.$$applyAsyncQueue = [];
    this.$$applyAsyncId = null;     // 保存遍历 $$applyAsyncQueue 队列的 setTimeout 的 id
    this.$$postDigestQueue = [];
}

Scope.prototype.$watch = function (watchFn, listenerFn, valueEq) {
    let watcher = {
        watchFn: watchFn,
        listenerFn: listenerFn || function () {},       // 防止 listenerFn 为 undefined 的情况
        last: initWatchVal,                      // 保存最近一次的值
        valueEq: !!valueEq            //  true 为按值比较，false 为按引用比较
    }

    this.$$watchers.push(watcher);
    // this.$$lastDirtyWatch = null;
}

Scope.prototype.$digest = function () {
    let dirty;
    let TTL = 10;       // TTL Time To Live，最多遍历的次数
    this.$$lastDirtyWatch = null;
    this.$beginPhase("$digest");

    if (this.$$flushApplyAsync) {
        clearTimeout(this.$$applyAsyncId);
        this.$$flushApplyAsync();
    }

    do {
        while (this.$$asyncQueue.length) {
            try {
                let asyncTask = this.$$asyncQueue.shift();
                asyncTask.scope.$eval(asyncTask.expression);
            } catch (e) {
                console.error(e);
            }
        }
        dirty = this.$$digestOnce();
        if ((dirty || this.$$asyncQueue.length) && !(TTL--)) {
            this.$clearPhase();
            throw new Error(`${10} digest iterations reached.`);
        }
        // count++;
    } while(dirty || this.$$asyncQueue.length);
    this.$clearPhase();

    while(this.$$postDigestQueue.length) {
        try {
            this.$$postDigestQueue.shift()();
        } catch(e) {
            console.error(e);
        }
    }
}

Scope.prototype.$$digestOnce = function () {
    let newValue, oldValue, dirty;

    for (let i = 0; i < this.$$watchers.length; i++) {
        try {
            let watcher = this.$$watchers[i];
            newValue = watcher.watchFn(this);      // this 指 Scope 对象
            oldValue = watcher.last;
            if (!this.$$areEqual(newValue, oldValue, watcher.valueEq)) {            // 只有 newValue 和 oldValue 不同时才执行 listenerFn
                this.$$lastDirtyWatch = watcher;
                watcher.last = watcher.valueEq ? _.cloneDeep(newValue) : newValue;
                // 如果 oldValue 是 initWatchVal 函数的引用，则将 oldValue = newValue
                // 因为 oldValue 可以被外部获取，防止私有函数 initWatchVal 泄露
                oldValue = oldValue === initWatchVal ? newValue : oldValue;
                watcher.listenerFn(newValue, oldValue, this);

                // 如果 newValue 不等于 oldValue，则 dirty 为 true
                dirty = true;
            } else if (this.$$lastDirtyWatch === watcher) {
                dirty = false;
                return dirty;
            }
        } catch (e) {
            console.error(e);
        }
    }
    return dirty;
}

// 使用一个函数变量作为初始值，因为是引用，只要作为私有变量，就不会出现重复
function initWatchVal () {}

Scope.prototype.$$areEqual = function (newValue, oldValue, valueEq) {
    if (valueEq) {
        return _.isEqual(newValue, oldValue);
    } else {
        return newValue === oldValue || 
            (typeof newValue === 'number' && 
             typeof oldValue === 'number' &&
             isNaN(newValue) && isNaN(oldValue));
    }
}

Scope.prototype.$eval = function (expr, locals) {
    return expr(this, locals);
}

Scope.prototype.$apply = function (expr) {
    try {
        this.$beginPhase("$apply");
        return this.$eval(expr);
    } finally {
        this.$clearPhase();
        this.$digest();
    }
}

Scope.prototype.$evalAsync = function (expr) {
    // 如果目前没有处于任何一个phase,且 $$asyncQueue 为空
    if (!this.$$phase && !this.$$asyncQueue.length) {
        setTimeout(() => {
            if (this.$$asyncQueue.length) this.$digest();
        }, 0);
    }
    this.$$asyncQueue.push({ scope: this, expression: expr});
}

Scope.prototype.$beginPhase = function (phase) {
    if (this.$$phase) {
        throw `${this.$$phase} already in progress.`;
    }
    this.$$phase = phase;
}

Scope.prototype.$clearPhase = function () {
    this.$$phase = null;
}

Scope.prototype.$applyAsync = function (expr) {
    this.$$applyAsyncQueue.push(() => {
        this.$eval(expr);
    });

    if (this.$$applyAsyncId === null) {
        this.$$applyAsyncId = setTimeout(() => {
            // 先执行完 $$applyAsyncQueue 队列中的所有函数，再启动一个 digest cycle
            this.$apply(this.$$flushApplyAsync.bind(this));
        }, 0);
    }
}

Scope.prototype.$$flushApplyAsync = function () {
    // 没有启动一个 digest cycle
    while (this.$$applyAsyncQueue.length) {
        try {
            this.$$applyAsyncQueue.shift()();
        } catch(e) {
            console.error(e);
        }
    }
    this.$$applyAsyncId = null;
}

Scope.prototype.$$postDigest = function (fn) {
    this.$$postDigestQueue.push(fn);
}